package com.wafer.action;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectUtil;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.wafer.bean.Env;
import com.wafer.db.DBColumn;
import com.wafer.db.DBUtils;
import com.wafer.domain.JavaField;
import com.wafer.ui.InfoDialog;
import com.wafer.util.FieldUtils;
import com.wafer.util.VelocityUtil;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * IntelliJ平台提供了动作的概念。一个动作是一个派生自AnAction类，当它的菜单项或工具栏按钮被选中时，它的actionPerformed()方法被调用
 */
public class MainAction extends AnAction {

    private static final Logger logger = LoggerFactory.getLogger(MainAction.class);

    private Project project;
    private String packageName = "";
    private String author;
    private String moduleName;
    private String className;
    private String tableName;
    private String description;
    private Boolean genController;
    private Boolean genService;
    private Boolean genMapper;
    private Boolean genServiceImpl;
    private Boolean genEntity;
    private Boolean genLombok;

    private List<JavaField> fieldList;
    private Set<String> importList;

    private enum CodeType {
        /**
         * 生成类型
         */
        Entity, Controller, Service, ServiceImpl, Mapper
    }

    /**
     * 单击CodeMenu菜单栏中的Code Generator触发方法，做一些配置初始化
     * @param e
     */
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        project = e.getData(LangDataKeys.PROJECT);
        /*String title = "标题";
        String msg = "hello world";
        Messages.showMessageDialog(project, msg, title, Messages.getInformationIcon());*/
        init(e);
    }

    /**
     * 刷新项目
     */
    private void refreshProject(AnActionEvent e) {
        Project project = e.getProject();
        if (project != null) {
            VirtualFile virtualFile = ProjectUtil.guessProjectDir(project);
            if (virtualFile != null) {
                virtualFile.refresh(false, true);
            }
        }
    }

    /**
     * 初始化Dialog
     */
    private void init(AnActionEvent e) {
        InfoDialog myDialog = new InfoDialog(genBean -> {
            MainAction.this.moduleName = genBean.getModuleName();
            MainAction.this.author = genBean.getAuthor();
            MainAction.this.packageName = genBean.getPackageName();
            MainAction.this.className = genBean.getClassName();
            MainAction.this.tableName = genBean.getTableName();
            MainAction.this.description = genBean.getDescription();
            MainAction.this.genController = genBean.getGenController();
            MainAction.this.genService = genBean.getGenService();
            MainAction.this.genMapper = genBean.getGenMapper();
            MainAction.this.genServiceImpl = genBean.getGenServiceImpl();
            MainAction.this.genEntity = genBean.getGenEntity();
            MainAction.this.genLombok = genBean.getGenLombok();

            initEnv();
            getFieldsFromDB();
            createClassFiles();
            refreshProject(e);
            Messages.showInfoMessage(project, "Create code success", "成功");
        });
        myDialog.setVisible(true);
    }

    private void initEnv() {
        Env.projectPath = project.getBasePath();
        Env.resourcesPath = getResourcesPath();
        Env.packageName = packageName;
        Env.author = author;
        Env.className = className;
        Env.description = description;
        Env.moduleName = moduleName;
        Env.tableName = tableName;
    }

    /**
     * 通过数据库获取字段信息
     */
    private void getFieldsFromDB() {
        List<DBColumn> columnList = DBUtils.getColumnList(tableName);
        fieldList = new ArrayList<>();
        importList = new HashSet<>();
        for (DBColumn column : columnList) {
            JavaField field = new JavaField();
            field.setName(FieldUtils.camel(column.getName()));
            field.setUpName(FieldUtils.upCamel(column.getName()));
            field.setColumnName(column.getName());
            field.setSize(column.getSize());
            field.setNullable(FieldUtils.jdbcNullable(column.getNullable()));
            String type = FieldUtils.type(column.getType());
            if ("Date".equals(type)) {
                importList.add("import java.util.Date;");
            }
            if ("BigDecimal".equals(type)) {
                importList.add("import java.math.BigDecimal;");
            }
            if ("LocalDateTime".equals(type)) {
                importList.add("import java.time.LocalDateTime;");
            }
            field.setType(type);
            field.setNote(column.getNote());
            fieldList.add(field);

            String jdbcType = FieldUtils.jdbcType(column.getType());
            if (column.isPk()) {
                if ("INTEGER".equalsIgnoreCase(jdbcType)) {
                    Env.idType = "java.lang.Integer";
                    Env.jdbcType = "INTEGER";
                } else if ("BIGINT".equalsIgnoreCase(jdbcType)) {
                    Env.idType = "java.lang.Long";
                    Env.jdbcType = "BIGINT";
                } else if ("VARCHAR".equalsIgnoreCase(jdbcType)) {
                    Env.idType = "java.lang.String";
                    Env.jdbcType = "VARCHAR";
                } else if ("LONGVARCHAR".equalsIgnoreCase(jdbcType)) {
                    Env.idType = "java.lang.String";
                    Env.jdbcType = "VARCHAR";
                }
            }
        }
    }

    /**
     * 生成类文件
     */
    private void createClassFiles() {
        if (genEntity) {
            createClassFile(CodeType.Entity);
        }
        if (genController) {
            createClassFile(CodeType.Controller);
        }
        if (genService) {
            createClassFile(CodeType.Service);
        }
        if (genServiceImpl) {
            createClassFile(CodeType.ServiceImpl);
        }
        if (genMapper) {
            createClassFile(CodeType.Mapper);
        }
    }

    /**
     * 生成代码
     */
    private void createClassFile(CodeType codeType) {
        String fileName;
        String content;
        String appPath = getAppPath();
        switch (codeType) {
            case Entity:
                String dtoName, voName, dtoContent;
                if (genLombok) {
                    fileName = "TemplateEntityLombok.vm";
                    dtoName = "TemplateEntityDtoLombok.vm";
                    voName = "TemplateEntityVoLombok.vm";
                } else {
                    fileName = "TemplateEntity.vm";
                    dtoName = "TemplateEntityDto.vm";
                    voName = "TemplateEntityVo.vm";
                }
                //entity
                content = readTemplateFile(fileName);
                content = VelocityUtil.evaluate(content, fieldList, importList);
                writeToFile(content, appPath + "model", className + ".java");
                //dto
                dtoContent = readTemplateFile(dtoName);
                dtoContent = VelocityUtil.evaluate(dtoContent, fieldList, importList);
                writeToFile(dtoContent, appPath + "dto", className + "Dto.java");
                //vo
                dtoContent = readTemplateFile(voName);
                dtoContent = VelocityUtil.evaluate(dtoContent, fieldList, importList);
                writeToFile(dtoContent, appPath + "vo", className + "Vo.java");
                break;
            case Controller:
                fileName = "TemplateController.vm";
                content = readTemplateFile(fileName);
                Map<String, Object> params = new HashMap<>(2);
                content = VelocityUtil.evaluate(content, params);
                writeToFile(content, appPath + "controller", className + "Controller.java");
                break;
            case Service:
                fileName = "TemplateService.vm";
                content = readTemplateFile(fileName);
                content = VelocityUtil.evaluate(content, Collections.emptyMap());
                writeToFile(content, appPath + "service", "I" + className + "Service.java");
                break;
            case ServiceImpl:
                fileName = "TemplateServiceImpl.vm";
                content = readTemplateFile(fileName);
                Map<String, Object> map = new HashMap<>(8);
                map.put("list", fieldList);
                content = VelocityUtil.evaluate(content, map);
                writeToFile(content, appPath + "service/impl", className + "ServiceImpl.java");
                break;
            case Mapper:
                fileName = "TemplateDao.vm";
                content = readTemplateFile(fileName);
                content = VelocityUtil.evaluate(content, Collections.emptyMap());
                writeToFile(content, appPath + "dao", "I" + className + "Dao.java");
                break;
            default:
                logger.error("没有对应生成类型");
        }
    }

    /**
     * 获取包名文件路径
     */
    private String getAppPath() {
        String packagePath = packageName.replace(".", "/");
        String appPath;
        if (moduleName != null && !"".equals(moduleName)) {
            appPath = project.getBasePath() + "/" + moduleName + "/src/main/java/" + packagePath + "/";
        } else {
            appPath = project.getBasePath() + "/src/main/java/" + packagePath + "/";
        }
        return appPath;
    }

    /**
     * 获取resources文件路径
     */
    private String getResourcesPath() {
        String resourcesPath;
        if (moduleName != null && !"".equals(moduleName)) {
            resourcesPath = project.getBasePath() + "/" + moduleName + "/src/main/resources/";
        } else {
            resourcesPath = project.getBasePath() + "/src/main/resources/";
        }
        return resourcesPath;
    }

    /**
     * 读取模板文件中的字符内容
     *
     * @param fileName 模板文件名
     */
    private String readTemplateFile(String fileName) {
        InputStream in;
        in = this.getClass().getResourceAsStream("/template/" + fileName);
        String content = "";
        try {
            content = new String(readStream(in), StandardCharsets.UTF_8);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return content;
    }

    private byte[] readStream(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try (inputStream; outputStream) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return outputStream.toByteArray();
    }

    /**
     * 生成
     *
     * @param content   类中的内容
     * @param classPath 类文件路径
     * @param className 类文件名称
     */
    private void writeToFile(String content, String classPath, String className) {
        try {
            File folder = new File(classPath);
            if (!folder.exists()) {
                folder.mkdirs();
            }
            File file = new File(classPath + "/" + className);
            if (!file.exists()) {
                file.createNewFile();
            }

            Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8));
            writer.write(content);
            writer.flush();
            writer.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
